﻿/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2010>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using MyPhotoIndex.Controls;
using System.Collections.ObjectModel;
using System.Windows.Forms;
using MyPhotoIndex.Interfaces;
using MyPhotoIndex.Interfaces.Imaging;
using System.Drawing;
using MyPhotoIndex.Plugins.Magnifier.Properties;
using MyPhotoIndex.Interfaces.ImageFormatProviders;
using System.Threading;
using MyPhotoIndex.Utilities;
using MyPhotoIndex.Interfaces.Plugins;

namespace MyPhotoIndex.Plugins.Magnifier
{
    class MagnifierForm : AutoFadingForm
    {
        private PictureBox pictureBox1;
        private IApplicationContext m_applicationContext;
        private bool m_closing = false;
        private ReadOnlyCollection<int> m_selectedImages = null;
        private Image ms_mutipleimages_notice = Resources.multiple_images_notice;
        private Image ms_no_image_notice = Resources.no_image_notice;

        private Image m_image;
        private Image m_oldImage;
        private object m_syncLock = new object();
        private Rectangle m_oldBounds;
        private Size m_imageDrawSize;
        private Point m_lastMousePosition;
        private Point m_drawingPoint;
        private TableLayoutPanel tableLayoutPanel1;
        private ToolStrip toolStrip1;
        private ToolStripButton smallerToolStripButton;
        private ToolStripButton largerToolStripButton;
        private ToolStripButton fitScreenToolStripButton;
        private ToolStripButton actualSizeToolStripButton;
        private bool m_valid = true;
        private int m_lastImageIdSelected;
        private float m_zoom = 0;
        private SelectedImageInfo m_lastLoadingImageInfo;

        private StatusStrip statusStrip1;
        ToolStripControlHost trackBarContainer;

        private void InitializeComponent()
        {
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
            this.smallerToolStripButton = new System.Windows.Forms.ToolStripButton();
            this.largerToolStripButton = new System.Windows.Forms.ToolStripButton();
            this.fitScreenToolStripButton = new System.Windows.Forms.ToolStripButton();
            this.actualSizeToolStripButton = new System.Windows.Forms.ToolStripButton();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            this.tableLayoutPanel1.SuspendLayout();
            this.toolStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // pictureBox1
            // 
            this.pictureBox1.BackColor = System.Drawing.Color.White;
            this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.pictureBox1.Location = new System.Drawing.Point(3, 24);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(230, 156);
            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pictureBox1.TabIndex = 2;
            this.pictureBox1.TabStop = false;
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 1;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.Controls.Add(this.toolStrip1, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.pictureBox1, 0, 1);
            this.tableLayoutPanel1.Controls.Add(this.statusStrip1, 0, 2);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 3;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.55556F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 88.44444F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(236, 204);
            this.tableLayoutPanel1.TabIndex = 4;
            // 
            // toolStrip1
            // 
            this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.smallerToolStripButton,
            this.largerToolStripButton,
            this.fitScreenToolStripButton,
            this.actualSizeToolStripButton});
            this.toolStrip1.Location = new System.Drawing.Point(0, 0);
            this.toolStrip1.Name = "toolStrip1";
            this.toolStrip1.Size = new System.Drawing.Size(236, 21);
            this.toolStrip1.Stretch = true;
            this.toolStrip1.TabIndex = 4;
            this.toolStrip1.Text = "toolStrip1";
            // 
            // smallerToolStripButton
            // 
            this.smallerToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
            this.smallerToolStripButton.Image = global::MyPhotoIndex.Plugins.Magnifier.Properties.Resources.fs_ZoomDown;
            this.smallerToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.smallerToolStripButton.Name = "smallerToolStripButton";
            this.smallerToolStripButton.Size = new System.Drawing.Size(23, 18);
            this.smallerToolStripButton.Text = "Smaller";
            // 
            // largerToolStripButton
            // 
            this.largerToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
            this.largerToolStripButton.Image = global::MyPhotoIndex.Plugins.Magnifier.Properties.Resources.fs_ZoomUp;
            this.largerToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.largerToolStripButton.Name = "largerToolStripButton";
            this.largerToolStripButton.Size = new System.Drawing.Size(23, 18);
            this.largerToolStripButton.Text = "Larger";
            // 
            // fitScreenToolStripButton
            // 
            this.fitScreenToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
            this.fitScreenToolStripButton.Image = global::MyPhotoIndex.Plugins.Magnifier.Properties.Resources.fs_ActualSize;
            this.fitScreenToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.fitScreenToolStripButton.Name = "fitScreenToolStripButton";
            this.fitScreenToolStripButton.Size = new System.Drawing.Size(23, 18);
            this.fitScreenToolStripButton.Text = "Fit to screen";
            // 
            // actualSizeToolStripButton
            // 
            this.actualSizeToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
            this.actualSizeToolStripButton.Image = global::MyPhotoIndex.Plugins.Magnifier.Properties.Resources.fs_FitScreen;
            this.actualSizeToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.actualSizeToolStripButton.Name = "actualSizeToolStripButton";
            this.actualSizeToolStripButton.Size = new System.Drawing.Size(23, 18);
            this.actualSizeToolStripButton.Text = "Actual size";
            // 
            // statusStrip1
            // 
            this.statusStrip1.Location = new System.Drawing.Point(0, 183);
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(236, 21);
            this.statusStrip1.TabIndex = 5;
            this.statusStrip1.Text = "statusStrip1";
            // 
            // MagnifierForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.BackColor = System.Drawing.Color.White;
            this.ClientSize = new System.Drawing.Size(236, 204);
            this.Controls.Add(this.tableLayoutPanel1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "MagnifierForm";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.Text = "Magnifier";
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel1.PerformLayout();
            this.toolStrip1.ResumeLayout(false);
            this.toolStrip1.PerformLayout();
            this.ResumeLayout(false);

        }

        public MagnifierForm(IApplicationContext applicationContext)
        {
            InitializeComponent();

            m_applicationContext = applicationContext;

            pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
            pictureBox1.MouseMove += new MouseEventHandler(pictureBox1_MouseMove);
            pictureBox1.MouseUp += new MouseEventHandler(pictureBox1_MouseUp);
            pictureBox1.MouseDown += new MouseEventHandler(pictureBox1_MouseDown);

            smallerToolStripButton.Click += new EventHandler(smallerToolStripButton_Click);
            largerToolStripButton.Click += new EventHandler(largerToolStripButton_Click);
            actualSizeToolStripButton.Click += new EventHandler(actualSizeToolStripButton_Click);
            fitScreenToolStripButton.Click += new EventHandler(fitScreenToolStripButton_Click);
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
        }

        public void CloseForm()
        {
            m_closing = true;
            this.Close();
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            this.Visible = false;

            if (m_closing == false)
            {
                e.Cancel = true;
            }
        }

        internal void SelectionChanged(ReadOnlyCollection<int> selectedImages, SelectedImageInfo lastSelected)
        {
            m_selectedImages = selectedImages;
            DrawMagnifier(lastSelected);
        }

        private void DrawMagnifier(SelectedImageInfo lastSelected)
        {
            if (pictureBox1.Image != null)
            {
                if (pictureBox1.Image != ms_mutipleimages_notice)
                {
                    pictureBox1.Image.Dispose();
                }

                pictureBox1.Image = null;
            }

            if (m_selectedImages.Count > 1)
            {
                pictureBox1.Image = ms_mutipleimages_notice;
                return;
            }

            if (m_selectedImages.Count == 0)
            {
                pictureBox1.Image = ms_no_image_notice;
                return;
            }

            if (m_selectedImages != null &&
                m_selectedImages.Count > 0)
            {
                int imageId = m_selectedImages[0];
                if (m_lastImageIdSelected != imageId)
                {
                    IImageData imageData = m_applicationContext.ImageCollection[imageId];
                    m_lastLoadingImageInfo = lastSelected;

                    Thread t = new Thread(new ParameterizedThreadStart(ImageChangedAsync));
                    t.Start(imageData);
                }
                else
                {
                    UpdateImageLocation(lastSelected);
                }

                m_lastImageIdSelected = imageId;
            }
        }

        private void UpdateImageLocation(SelectedImageInfo lastSelected)
        {
            if (lastSelected.SelectionMethod == SelectedImageSelectionMethod.Mouse)
            {
                Size fitSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF(pictureBox1.Width, pictureBox1.Height));

                if (fitSize.Width >= m_imageDrawSize.Width - 50 &&
                    fitSize.Height >= m_imageDrawSize.Height - 50)
                {
                    return;
                }

                PointF clickPoint = new Point();
                clickPoint.X = ((float)m_imageDrawSize.Width / lastSelected.ClickLocationRatio.X);
                clickPoint.Y = ((float)m_imageDrawSize.Height / lastSelected.ClickLocationRatio.Y);

                clickPoint.X -= (pictureBox1.Width / 2);
                clickPoint.Y -= (pictureBox1.Height / 2);

                m_drawingPoint = new Point((int)clickPoint.X * -1, (int)clickPoint.Y * -1);
                pictureBox1.Invalidate();
            }
        }

        private void ImageChangedAsync(object state)
        {
            IImageData imageData = (IImageData)state;
            if (imageData.LinkStatus == ImageLinkStatus.Valid)
            {
                lock (m_syncLock)
                {
                    m_oldBounds = new Rectangle(m_drawingPoint, m_imageDrawSize);

                    IImageFormatProvider provider = imageData.FormatProvider;
                    Image newImage = provider.LoadImage(imageData);
                    if (m_image != null)
                    {
                        m_image.Dispose();
                        m_image = null;
                    }

                    m_image = newImage;

                    if (m_valid == true)
                    {
                        try
                        {
                            this.Invoke(new ThreadStart(ImageChanged));
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }

        private void ImageChanged()
        {
            if (m_image != null)
            {
                bool isSmall = false;
                if (ImageAlgo.IsLandscapeImage(m_image.Size))
                {
                    if (m_image.Width < pictureBox1.Width &&
                       m_image.Height < pictureBox1.Height)
                    {
                        isSmall = true;
                    }
                }
                else
                {
                    if (m_image.Height < pictureBox1.Width &&
                       m_image.Width < pictureBox1.Height)
                    {
                        isSmall = true;
                    }
                }

                if (isSmall == true)
                {
                    UseActualSize(false);
                }
                else
                {
                    if (m_zoom > 0)
                    {
                        RestoreZoom(false);
                    }
                    else
                    {
                        FitScreen(false);
                    }
                }

                if (m_lastLoadingImageInfo != null)
                {
                    UpdateImageLocation(m_lastLoadingImageInfo);
                }

                pictureBox1.Refresh();
            }
        }

        private void RestoreZoom(bool redraw)
        {
            m_imageDrawSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF((float)m_image.Width * m_zoom, (float)m_image.Height * m_zoom));

            CenterImage();
            UpdateZoomValues();

            if (redraw)
            {
                pictureBox1.Refresh();
            }
        }

        internal void UseActualSize()
        {
            UseActualSize(true);
        }

        internal void UseActualSize(bool redraw)
        {
            m_imageDrawSize = m_image.Size;

            //    m_toolstrip.ImageZoomPercent = 100;

            CenterImage();

            if (redraw == true)
            {
                pictureBox1.Refresh();
            }

            UpdateZoomValues();
        }

        internal void FitScreen()
        {
            FitScreen(true);
        }

        internal void FitScreen(bool redraw)
        {
            if (m_image == null)
            {
                return;
            }

            m_zoom = 0;
            m_imageDrawSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF(pictureBox1.Width, pictureBox1.Height));

            CenterImage();
            UpdateZoomValues();

            if (redraw == true)
            {
                pictureBox1.Refresh();
            }
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (m_imageDrawSize.Width <= pictureBox1.Width ||
                    m_imageDrawSize.Height <= pictureBox1.Height)
                {
                    return;
                }

                m_drawingPoint.X += (e.Location.X - m_lastMousePosition.X);
                m_drawingPoint.Y += (e.Location.Y - m_lastMousePosition.Y);

                int maxX = (pictureBox1.Width - m_imageDrawSize.Width);
                int maxY = (pictureBox1.Height - m_imageDrawSize.Height);

                if (m_drawingPoint.X > 0)
                {
                    m_drawingPoint.X = 0;
                }

                if (m_drawingPoint.Y > 0)
                {
                    m_drawingPoint.Y = 0;
                }

                if (m_drawingPoint.X < maxX)
                {
                    m_drawingPoint.X = maxX;
                }

                if (m_drawingPoint.Y < maxY)
                {
                    m_drawingPoint.Y = maxY;
                }

                m_lastMousePosition = e.Location;
                pictureBox1.Refresh();
            }
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                m_lastMousePosition = e.Location;

                if (m_imageDrawSize.Width > pictureBox1.Width ||
                    m_imageDrawSize.Height > pictureBox1.Height)
                {
                    this.Cursor = Cursors.Hand;
                }
            }
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.Cursor == Cursors.Hand)
            {
                this.Cursor = Cursors.Default;
            }
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (m_image != null &&
                m_valid == true)
            {
                e.Graphics.DrawImage(m_image, new Rectangle(m_drawingPoint, m_imageDrawSize));
            }
        }

        internal void ZoomIn()
        {
            int stepX = m_imageDrawSize.Width / 10;
            int stepY = m_imageDrawSize.Height / 10;
            m_imageDrawSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF(m_imageDrawSize.Width + stepX, m_imageDrawSize.Height + stepY));

            CenterImage();

            UpdateZoomValues();
            pictureBox1.Refresh();

        }

        internal void ZoomOut()
        {
            int stepX = m_imageDrawSize.Width / 10;
            int stepY = m_imageDrawSize.Height / 10;

            Size fitSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF(pictureBox1.Width, pictureBox1.Height));
            int newWidth = Math.Max(m_imageDrawSize.Width - stepX, fitSize.Width);
            int newHeight = Math.Max(m_imageDrawSize.Height - stepY, fitSize.Height);

            m_imageDrawSize = ImageAlgo.GetImageProportionalSize(new SizeF(m_image.Width, m_image.Height), new SizeF(newWidth, newHeight));

            CenterImage();

            UpdateZoomValues();
            pictureBox1.Refresh();
        }

        internal void UpdateZoomValues()
        {
            m_zoom = ((float)m_imageDrawSize.Width / (float)m_image.Width);
            float zoomPercent = (m_zoom * 100);
            //    m_toolstrip.ImageZoomPercent = (int)zoomPercent;
        }

        internal void CenterImage()
        {
            int X = (pictureBox1.Width - m_imageDrawSize.Width) / 2;
            int Y = (pictureBox1.Height - m_imageDrawSize.Height) / 2;
            m_drawingPoint = new Point(X, Y);
        }

        private void smallerToolStripButton_Click(object sender, EventArgs e)
        {
            ZoomOut();
        }

        private void largerToolStripButton_Click(object sender, EventArgs e)
        {
            ZoomIn();
        }

        void fitScreenToolStripButton_Click(object sender, EventArgs e)
        {
            FitScreen();
        }

        void actualSizeToolStripButton_Click(object sender, EventArgs e)
        {
            UseActualSize();
        }
    }
}
